package com.sky.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.constant.MessageConstant;
import com.sky.constant.PasswordConstant;
import com.sky.constant.StatusConstant;
import com.sky.context.BaseContext;
import com.sky.dto.EmployeeDTO;
import com.sky.dto.EmployeeLoginDTO;
import com.sky.dto.EmployeePageQueryDTO;
import com.sky.entity.Employee;
import com.sky.exception.AccountLockedException;
import com.sky.exception.AccountNotFoundException;
import com.sky.exception.PasswordErrorException;
import com.sky.mapper.EmployeeMapper;
import com.sky.result.PageResult;
import com.sky.service.EmployeeService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;

import java.time.LocalDateTime;
import java.util.List;

@Service
public class EmployeeServiceImpl implements EmployeeService {

    @Autowired
    private EmployeeMapper employeeMapper;

    /**
     * 员工登录
     *
     * @param employeeLoginDTO
     * @return
     */
    public Employee login(EmployeeLoginDTO employeeLoginDTO) {
        String username = employeeLoginDTO.getUsername();
        String password = employeeLoginDTO.getPassword();

        //1、根据用户名查询数据库中的数据
        Employee employee = employeeMapper.getByUsername(username);

        //2、处理各种异常情况（用户名不存在、密码不对、账号被锁定）
        if (employee == null) {
            //账号不存在
            throw new AccountNotFoundException(MessageConstant.ACCOUNT_NOT_FOUND);
        }

        //密码比对
        // TODO 后期需要进行md5加密，然后再进行比对。例如有时候有后续的工作还没完成和遗留的问题就可以加这个标识，在idea下面会有todo的标签栏来提醒,已经解决。
        password = DigestUtils.md5DigestAsHex(password.getBytes());
        if (!password.equals(employee.getPassword())) {
            //密码错误
            throw new PasswordErrorException(MessageConstant.PASSWORD_ERROR);
        }

        if (employee.getStatus() == StatusConstant.DISABLE) {
            //账号被锁定
            throw new AccountLockedException(MessageConstant.ACCOUNT_LOCKED);
        }

        //3、返回实体对象
        return employee;
    }

    /**
     * 新增员工
     *
     * 在Service中获取线程局部变量中的值：
     *
     * 1. 账号必须是唯一的
     * 2. 手机号为合法的11位手机号码
     * 3. 身份证号为合法的18位身份证号码
     * 4. 密码默认为123456
     *
     * @param employeeDTO
     */
    @Override
    public void save(EmployeeDTO employeeDTO) {

        //客户端每一次发起的请求，都会对应一个单独的线程，输出当前线程的id
        System.out.println("EmployeeServiceImpl的方法save，当前线程的id："+Thread.currentThread().getId());

        //传进来的是一个dto，为了方便封装前端提交过来的数据，但是传给持久层，还是建议用实体类
        //所以做一个对象的转换，把dto转成实体
        Employee employee = new Employee();

        //对象属性拷贝(前提是属性名一致)
        BeanUtils.copyProperties(employeeDTO,employee);   //前面的拷贝给后面的，注意顺序

        //设置账户的状态，默认正常状态（1：正常，0：锁定）,避免硬编码（直接写数字1表示账户状态，定义了常量类）
        employee.setStatus(StatusConstant.ENABLE);

        //设置密码，默认密码为123456，登录是要将密码加密后和数据库对比，所以设置的密码要md5加密后存进去
        employee.setPassword(DigestUtils.md5DigestAsHex(PasswordConstant.DEFAULT_PASSWORD.getBytes()));

        /*
        AutoFillAspect已经实现了这些公共字段的填充
        //设置当前记录的创建时间，修改时间
        employee.setCreateTime(LocalDateTime.now());
        employee.setUpdateTime(LocalDateTime.now());

        //设置当前记录创建人的id和修改人的id
        //在Service中获取线程局部变量中的值：
        //TODO  后期需要把下面暂时写的10l改成登录用户的id,已经完成
        employee.setCreateUser(BaseContext.getCurrentId());
        employee.setUpdateUser(BaseContext.getCurrentId());
        */

        System.out.println("新增{}员工成功"+employee.getUsername());

        employeeMapper.insert(employee);

    }

    /**
     * 分页查询
     *
     * - 请求参数类型为Query，不是json格式提交，在路径后直接拼接。/admin/employee/page?name=zhangsan
     * - 返回数据中records数组中使用Employee实体类对属性进行封装。
     *
     * - 根据页码展示员工信息
     * - 每页展示10条数据
     * - 分页查询时可以根据需要，输入员工姓名进行查询
     *
     * 员工分页查询的前端结果显示：最后操作时间格式不清晰，操作时间字段显示有问题如2022524112934：
     *      解决方式一：在属性上加上注解，对日期进行格式化   @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
     *                                              private LocalDateTime updateTime;
     *      解决方式二：在WebMvcConfiguration中扩展SpringMVC的消息转换器，统一对日期类型进行格式处理
     *
     * @param employeePageQueryDTO
     * @return
     */
    @Override
    public PageResult pageQuery(EmployeePageQueryDTO employeePageQueryDTO) {
        //select * from employee limit 0,10。
        // dto把页码和每页的记录数传过来了，然后就可以动态的把这两个参数给就算出来给它拼接到sql语句去。这样写的话，就要自己去计算自己去拼接，这里用上了mybatis框架提供的插件pagehelper

        //开始分页查询
        PageHelper.startPage(employeePageQueryDTO.getPage(), employeePageQueryDTO.getPageSize());
        //页码，和每页的记录数会自动的拼接到sql语句里面去进行一个动态的拼接，类似于mybatis的动态sql，会动态的把limit关键字拼接进去(在EmployeeMapper.xml文件的sql里的select * from employee)

        //这个插件要求我们后边的方法返回值是固定的，泛型是employee实体。pageQuery方法和startPage等等之间的联系，得看源码
        Page<Employee> page = employeeMapper.pageQuery(employeePageQueryDTO);   //还有个员工姓名name要动态的拼接到sql语句里，所以用dto参数

        //employeeMapper调用的方法返回的是一个page对象，而我们想要的是一个PageResult对象，所以要加工处理。
        //而PageResult对象要构造的时候要total和records两个参数。所以要拿到PageResult里面的total和records，而返回的page对象可以拿到这两个值
        long total = page.getTotal();
        List<Employee> records = page.getResult();

        //两个参数拿到了，就封装到new的PageResult对象里，PageResult里加了注释会生成有参的构造方法，参数进去了会调用有参构造器
        return new PageResult(total,records);

    }

    /**
     * 启用禁用员工账号
     * 在员工管理列表页面，可以对某个员工账号进行启用或者禁用操作。账号禁用的员工不能登录系统，启用后的员工可以正常登录。
     * 如果某个员工账号状态为正常，则按钮显示为 "禁用"，如果员工账号状态为已禁用，则按钮显示为"启用"。
     *
     * - 可以对状态为“启用” 的员工账号进行“禁用”操作
     * - 可以对状态为“禁用”的员工账号进行“启用”操作
     * - 状态为“禁用”的员工账号不能登录系统
     *
     * 1). 路径参数携带状态值。请求参数有两个，路径参数和查询参数query
     * 2). 同时，把id传递过去，明确对哪个用户进行操作。并且id的参数类型是query类型，是查询参数，通过地址栏传参的方式把id传过去
     * 3). 返回数据中code状态是必须，其它是非必须。
     *
     * @param status
     * @param id
     */
    @Override
    public void startOrStop(Integer status, Long id) {
        //希望持久层mapper中执行语句：update employee set status =? where id =?，写成动态的update通用性更强，可以根据传进来的参数不同来执行多个字段

        /*Employee employee = new Employee();
        employee.setStatus(status);
        employee.setId(id);*/

        //加了builder注解,就不用上面那种传统的写法
        Employee employee = Employee.builder()
                .status(status)
                .id(id)
                .build();

        //传一个实体类更合适
        employeeMapper.update(employee);
    }

    /**
     * 根据id查询员工信息,回显员工信息功能。eg:前端点击某个id对应的员工的修改时，能够回显该id对应员工的信息
     *
     * 在员工管理列表页面点击 "编辑" 按钮，跳转到编辑页面，在编辑页面回显员工信息并进行修改，最后点击 "保存" 按钮完成编辑操作。
     * 根据上述原型图分析，编辑员工功能涉及到两个接口：
     * - 根据id查询员工信息
     * - 编辑员工信息
     *
     * @param id
     * @return
     */
    @Override
    public Employee getById(Long id) {
        Employee employee = employeeMapper.getById(id);
        //根据id查询到的employee返回过来也包含密码，可能也会传输给前端，为了不让加密的密码显示出来，所以额外替换掉
        employee.setPassword("****");
        return employee;
    }
    /**
     * 编辑员工信息
     *
     * 根据上述原型图分析，编辑员工功能涉及到两个接口：
     * - 根据id查询员工信息
     * - 编辑员工信息
     *
     * 在实现**启用禁用员工账号**功能时，已实现employeeMapper.update(employee)，在此不需写Mapper层代码。
     *
     * @param employeeDTO
     */
    @Override
    public void update(EmployeeDTO employeeDTO) {
        //调用持久层mapper的update方法，但是update做了一个动态的sql语句，在xml文件里parameterType="Employee"，而不是employeeDTO
        Employee employee = new Employee();
        //所以需要做一个强转employeeDTO转employee
        BeanUtils.copyProperties(employeeDTO,employee);

        /*
                AutoFillAspect已经实现了这些公共字段的填充
        //是修改操作，修改时间和修改人，因为原来的dto里面没有这两个属性
        employee.setUpdateTime(LocalDateTime.now());
        //通过ThreadLocal获得修改人的id,在拦截器JwtTokenAdminInterceptor中设置好了id的值代码： BaseContext.setCurrentId(empId);
        employee.setUpdateUser(BaseContext.getCurrentId());
*/
        employeeMapper.update(employee);
    }


}
